package com.covery.config;

import com.covery.FileUtils;
import com.covery.dto.SameFile;
import lombok.val;
import org.apache.commons.codec.digest.DigestUtils;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author ：txw
 * copy 备份
 * @since ：Created in 2020/5/24 0024 12:17
 */
public class QuartzJob1 extends QuartzJobBean {
    /**
     * right 目录hash
     */
    private static final String saveFileName = "hashMap.sav";
    private static Map<String,String> FILE_SASE_DIC = new HashMap<>(16);
    private static String lastLeftMd5 = null;
    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {

        JobDataMap dataMap = jobExecutionContext.getJobDetail().getJobDataMap();
        val initKey = (InitSetting)dataMap.get("initKey");
        SimpleDateFormat sdf=new SimpleDateFormat(initKey.getDateFormat());
        String name  = sdf.format(new Date());
        // 如果map 为空
        File fileMap =  new File(initKey.getCopyToFilePath()+"\\"+saveFileName);
        if(FILE_SASE_DIC.isEmpty() && fileMap.exists()) {
            // 加载 map
            try(InputStream inputStream = new FileInputStream(fileMap);
            ObjectInputStream objectInputStream = new ObjectInputStream(inputStream)){
                FILE_SASE_DIC= (Map<String,String>)objectInputStream.readObject();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        String bakDirPath = initKey.getCopyToFilePath()+"\\" + name;
        File file = new File(initKey.getOriginFilePath());
        Integer rSize = initKey.getRemoveLeftDirectory();
        int removeSize = rSize == null || rSize < 2 ? 2: rSize;
        boolean isCopy = false;
        // 查看右边是否为空
        boolean isNull = false;
        // 最新目录
        String lastModifyDic = null;
        List<String> oldDicFileList = new ArrayList<>(16);
        // 如果一组存档和最新一个存档不匹配，且和之前的存档有个匹配，说明该文件回归
        File origin = new File(initKey.getCopyToFilePath());
        if (!origin.exists()) {
            boolean success  = origin.mkdirs();
            if (!success) {
                throw new RuntimeException("创建目录失败");
            }
            isNull = true;
        } else {
           val  listOrigin = origin.listFiles();
           if (listOrigin == null || listOrigin.length == 0) {
               isNull = true;
           } else {
               // 取最新的一个目录
               String lastName;
               if (listOrigin[0].isDirectory()) {
                   lastName = listOrigin[0].getName();
                   lastModifyDic = listOrigin[0].getPath();
               } else {
                   lastName = listOrigin[1].getName();
                   lastModifyDic = listOrigin[1].getPath();
               }
                for (File file1: listOrigin) {
                    if (file1.isDirectory() && lastName.compareTo(file1.getName()) < 0) {
                        lastName = file1.getName();
                        lastModifyDic = file1.getPath();
                    }
                    if (file.isDirectory()) {
                        oldDicFileList.add(file1.getName());
                    }
                }

           }
        }
        if (!oldDicFileList.isEmpty()) {
            oldDicFileList.sort(Comparator.comparing(g->g));
            oldDicFileList.remove(oldDicFileList.size()-1);
        }
        if (isNull) {

            copyDir(initKey, bakDirPath,null,null, oldDicFileList);
        } else {
            if (file.exists()) {
                // 备份
                val f = file.listFiles();
                List<String> md5Right = new ArrayList<>(16);
                List<String> md5Left = new ArrayList<>(16);
                if (f != null) {
                    boolean firstFlag = true;
                    boolean copyFlag = true;

                    for (File cp : f) {
                            // 和右边比较
                        File right = null;
                        if (InitSetting.YES.equals(initKey.getCopyOne())) {
                            if (cp.getName().contains(initKey.getCopyOneFileNamePrefix())) {
                                right = new File(lastModifyDic + "\\" + cp.getName());
                            }
                        } else {
                            right = new File(lastModifyDic + "\\" + cp.getName());
                        }
                            if (right != null && right.exists()) {
                                    val isSame = isSameFiles(cp,right);
                                    md5Right.add(isSame.getMd5Right());
                                    md5Left.add(isSame.getMd5Left());
                                    if (!isSame.isSame() && copyFlag) {
                                        isCopy = true;
                                        copyFlag = false;
                                    } else if (firstFlag && !isSame.isSame()){
                                        firstFlag = false;
                                    }

                            }
                    }
                    // md5Right

                }
                // 备份老的
                if (isCopy) {

                    copyDir(initKey, bakDirPath, md5Right,md5Left,oldDicFileList);
                }
            }
        }
        // 备份
        // 删除
        File removeFile = new File(initKey.getCopyToFilePath());
        if (removeFile.exists()) {
            val list = removeFile.listFiles();
            if (list != null && list.length > 2) {
                int n = list.length - removeSize;
                // 在 m 中找到最小的n 个 并删除
                for (int i = 0; i < n; i++) {
                    if (list[i].isDirectory()) {
                        FileUtils.deleteDirectory(list[i].getPath());
                    }
                }
            }
        }


    }



    private String parseMd5List(List<String> list) {
        if (list != null) {
            StringBuilder stringBuilder = new StringBuilder();
            for (String s : list) {
                stringBuilder.append(s);
            }
            return DigestUtils.md5Hex(stringBuilder.toString());
        }
        return null;
    }
    private static String leftMd5Hex(File fileByte1) {
        try (InputStream inputStream = new FileInputStream(fileByte1)){
            return DigestUtils.md5Hex(inputStream);
        }catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 验证两个文件字节流是否相等
     * @return boolean true 相等
     */
    private static SameFile isSameFiles(File fileByte1, File fileByte2)  {
        try (InputStream inputStream = new FileInputStream(fileByte1);
        InputStream right = new FileInputStream(fileByte2)){
            String firstFileMd5 = DigestUtils.md5Hex(inputStream);

            String  secondFileMd5 = DigestUtils.md5Hex(right);
            val same = new SameFile(firstFileMd5,secondFileMd5);
            if (firstFileMd5.equals(secondFileMd5)) {
                System.out.println("---- equals ------ md5 " + firstFileMd5);
                same.setSame(true);
            } else {
                System.out.println(firstFileMd5 + " is firstFileMd5 ++ unequal ++ secondFileMd5 = " + secondFileMd5);
                same.setSame(false);
            }
            return same;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new SameFile();
    }


    private boolean oldDicNameHasSame(List<String> oldDicName, String nowName){
        for (String s: oldDicName) {
            if (nowName.equals(s)) {
                return true;
            }
        }
        return false;
    }

    private void copyDir(InitSetting initSetting,String dest,List<String> rightMd5, List<String> leftMd5, List<String> oldDic){
        // 判断是否复制， 右边最新目录md5 如果 不等于 左边  ，且 右边 其他 mud5 等于 左边 不复制
        if (InitSetting.YES.equals(initSetting.getBeforeBak())) {
            if (rightMd5 != null) {
                // 右边 为 null 直接跳过
                if (leftMd5 == null) {
                    leftMd5 = getLeftMd5(initSetting);
                }
                leftMd5.sort(Comparator.comparing(g-> g));
                val md5S = parseMd5List(leftMd5);
                if (lastLeftMd5 != null && lastLeftMd5.equals(md5S)) {
                    return;
                }
                // 进入这里，说明 最新目录不等于 左边
                val map = FILE_SASE_DIC.get(md5S);
                if (map != null && oldDicNameHasSame(oldDic, map)) {
                    return;
                }
            }
        }
        File file1 = new File(dest);
        String pathName = file1.getName();
        if (!file1.exists()) {
            boolean success  = file1.mkdirs();
            if (!success) {
                throw new RuntimeException("创建目录失败");
            }
        }
        File sourceDir = new File(initSetting.getOriginFilePath());
        List<String> leftHash;
        if (leftMd5 != null) {
            leftHash = leftMd5;
        } else {
            leftHash = getLeftMd5(initSetting);
        }
        val list = sourceDir.listFiles();
        if (list != null) {
            for (File file: list) {
                // 复制一组存档文件
                if (InitSetting.YES.equals(initSetting.getCopyOne())) {
                    if (file.getName().contains(initSetting.getCopyOneFileNamePrefix())) {
                        copy(file, dest + "\\" + file.getName());

                    }
                } else {
                    copy(file, dest + "\\" + file.getName());
                }

            }
        }
        leftHash.sort(Comparator.comparing(g-> g));
        val md5S = parseMd5List(leftHash);
        // 永远复制左边 的 md5,加上右边的目录
        lastLeftMd5 = md5S;
        FILE_SASE_DIC.put(md5S, pathName);
        try (OutputStream outputStream = new FileOutputStream(initSetting.getCopyToFilePath()+"\\" + saveFileName)
                ;ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream)){
                objectOutputStream.writeObject(FILE_SASE_DIC);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private List<String> getLeftMd5(InitSetting initSetting) {
        val list = new ArrayList<String>(16);
        File sourceDir = new File(initSetting.getOriginFilePath());
        val fileList = sourceDir.listFiles();
        if (fileList != null) {
            for (File file : fileList) {
                if (InitSetting.YES.equals(initSetting.getCopyOne())) {
                    if (file.getName().contains(initSetting.getCopyOneFileNamePrefix())) {
                        list.add(leftMd5Hex(file));
                    }
                } else {
                    list.add(leftMd5Hex(file));
                }
            }
        }
        return list;
    }


    private void copy(File source, String dest) {
        int bufferSize = 1000;
        try (InputStream in = new FileInputStream(source);
             OutputStream out = new FileOutputStream((dest))){
            byte[] buffer = new byte[bufferSize];
            int len;

            while ((len = in.read(buffer)) > 0) {
                out.write(buffer, 0, len);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}